package com.geostar.constructionland.attachment.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.geostar.constructionland.attachment.entity.FileDownVo;
import com.geostar.constructionland.attachment.entity.GeoAttachment;
import com.geostar.constructionland.attachment.entity.RepEntity;
import com.geostar.constructionland.attachment.entity.TextExtractor;
import com.geostar.constructionland.attachment.entity.vo.UploadPreVo;
import com.geostar.constructionland.attachment.mapper.GeoAttachmentMapper;
import com.geostar.constructionland.attachment.service.IAttachmentService;
import com.geostar.constructionland.attachment.service.IChunkService;
import com.geostar.constructionland.attachment.service.IStorageService;
import com.geostar.constructionland.attachment.service.StorageServiceFactory;
import com.geostar.constructionland.attachment.util.LocalTmpPathUtil;
import com.mongodb.client.gridfs.model.GridFSFile;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.gridfs.GridFsResource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.*;
import java.util.Date;
import java.util.List;
import java.util.UUID;

/**
 * @author : yell
 * @Description: 文件上传处理类
 * @date : 2020/01/03 15:23
 */
@Service
@Slf4j
public class AttachmentServiceImpl implements IAttachmentService {
    private GeoAttachmentMapper geoAttachmentMapper;
    private IChunkService iChunkService;
    private LocalTmpPathUtil localTmpPathUtil;
    private StorageServiceFactory storageServiceFactory;

    @Autowired
    public void setGeoAttachmentMapper(GeoAttachmentMapper geoAttachmentMapper) {
        this.geoAttachmentMapper = geoAttachmentMapper;
    }

    @Autowired
    public void setiChunkService(IChunkService iChunkService) {
        this.iChunkService = iChunkService;
    }

    @Autowired
    public void setLocalTmpPathUtil(LocalTmpPathUtil localTmpPathUtil) {
        this.localTmpPathUtil = localTmpPathUtil;
    }

    @Autowired
    public void setStorageServiceFactory(StorageServiceFactory storageServiceFactory) {
        this.storageServiceFactory = storageServiceFactory;
    }

    @Override
    public UploadPreVo preUpload(GeoAttachment geoAttachment) {
        String md5 = geoAttachment.getMd5();
        long fileSize = geoAttachment.getFileSize();
        /**
         * 一、秒传
         * 查询是否有相同MD5的文件上传过,如果有相同的则提示已上传，即秒传
         * */
        List<GeoAttachment> uploadedAttachments =
                geoAttachmentMapper.selectList(
                        new LambdaQueryWrapper<GeoAttachment>()
                                .eq(GeoAttachment::getMd5, md5)
                                .eq(GeoAttachment::getFileSize, fileSize)
                                .orderByDesc(GeoAttachment::getLastModifiedDate));
        if (CollectionUtils.isNotEmpty(uploadedAttachments)) {
            /**存在相同md5文件,*/
            geoAttachment.setFilePath(uploadedAttachments.get(0).getFilePath());
            String uuid = UUID.randomUUID().toString().replace("-", "");
            geoAttachment.setId(uuid);
            geoAttachmentMapper.insert(geoAttachment);
            return new UploadPreVo(100, uuid, null);
        }
        /**
         * 二、分片上传
         * 1.查看文件是否需要分片
         * 2.如果文件需要分片，查看是否有前次上传信息，有则进行分片续传
         * **/
        if (geoAttachment.getFileSize() > geoAttachment.getChunkSize()) {
            /**该文件需要分片上传**/
            List<String> list = iChunkService.getReUploadChunkInfo(md5, geoAttachment.getChunkSize());
            if (list.size() > 0) {
                return new UploadPreVo(101, "分片续传", list);
            }
        }
        /**正常上传**/
        return new UploadPreVo(102, "请上传文件", null);
    }

    @Override
    public RepEntity uploadByChunks(GeoAttachment geoAttachment) {
        RepEntity repEntity = new RepEntity("400", "上传失败");
        boolean isOk = iChunkService.uploadChunk(geoAttachment);
        if (isOk) {
            repEntity = new RepEntity("200", "上传成功！");
        }
        return repEntity;
    }


    @Override
    public RepEntity upload(GeoAttachment geoAttachment) {
        String fileId = geoAttachment.getId();
        IStorageService storageService = storageServiceFactory.getCurrentService();
        byte[] bytes = geoAttachment.getFileContent();
        RepEntity repEntity = storageService.uploadAttach(bytes, geoAttachment.getFileSize(), geoAttachment.getName(),geoAttachment.getExt());
        if (repEntity.isOK()) {
            geoAttachment.setFilePath(repEntity.getContent());
            fileId = StringUtils.isNotEmpty(fileId) ? fileId : UUID.randomUUID().toString().replace("-", "");
            geoAttachment.setId(fileId);
            geoAttachmentMapper.insert(geoAttachment);
        }
        return new RepEntity("200", fileId);
    }


    @Override
    public FileDownVo downLoadFile(String attachId) {
        IStorageService storageService = storageServiceFactory.getCurrentService();
        FileDownVo fileDownVo = new FileDownVo();
        /**查找附件**/
        GeoAttachment geoAttachment = geoAttachmentMapper.selectById(attachId);
        if (geoAttachment != null && StringUtils.isNotEmpty(geoAttachment.getFilePath())) {
            byte[] bytes = storageService.getByteArrayByFilePath(geoAttachment.getFilePath());
            fileDownVo.setBytes(bytes);
            fileDownVo.setFileName(geoAttachment.getName());
            fileDownVo.setFileSize(geoAttachment.getFileSize());
        } else {
            /**查找mongodb*/
            MongoDBStorageServiceImpl mongoDBStorageService = null;
            try {
                mongoDBStorageService =
                        (MongoDBStorageServiceImpl) storageServiceFactory.getStorageService("mongoDBStorageService");
            } catch (Exception e) {
                e.printStackTrace();
            }
            GridFSFile gridFSFile = mongoDBStorageService.getGridFsFile(attachId);
            long fileSize = gridFSFile.getLength();
            String fileName = gridFSFile.getExtraElements().getString("fileName");
            String md5 = gridFSFile.getMD5();
            GridFsResource gridFsResource = mongoDBStorageService.getGridFsResource(gridFSFile);
            if (gridFsResource != null) {
                /**如果mongodb中含有此文件**/
                /***转存一份至fastdfs中*/
                try {
                    byte[] bytes = IOUtils.toByteArray(gridFsResource.getInputStream());
                    RepEntity repEntity = storageService.uploadAttach(bytes, fileSize, fileName,"");
                    if (repEntity.isOK()) {
                        geoAttachment = new GeoAttachment();
                        geoAttachment.setFilePath(repEntity.getContent());
                        geoAttachment.setId(attachId);
                        geoAttachment.setFileSize((int) fileSize);
                        geoAttachment.setLastModifiedDate(new Date(gridFsResource.lastModified()));
                        geoAttachment.setName(fileName);
                        geoAttachment.setMd5(md5);
                        geoAttachmentMapper.insert(geoAttachment);
                    }
                    fileDownVo.setBytes(bytes);
                    fileDownVo.setFileName(geoAttachment.getName());
                    fileDownVo.setFileSize(geoAttachment.getFileSize());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return fileDownVo;
    }

    @Override
    public RepEntity mergeChunks(GeoAttachment geoAttachment) {
        RepEntity repEntity = null;
        try {
            repEntity = iChunkService.mergeChunks(geoAttachment);
        } catch (Exception e) {
            e.printStackTrace();
            /**删除临时文件夹**/
            deleteDirectory(localTmpPathUtil.getFileTmpDirPath(geoAttachment.getMd5(), geoAttachment.getChunkSize(), false));
        }
        if (repEntity != null && repEntity.isOK()) {
            /**合并成功后，上传至文件服务器中**/
            IStorageService storageService = storageServiceFactory.getCurrentService();
            log.info("fileSize:{},fileSize2:{}",File2byte(repEntity.getContent()).length,geoAttachment.getFileSize());
            RepEntity rep = storageService.uploadAttach(File2byte(repEntity.getContent()), geoAttachment.getFileSize(), geoAttachment.getName(),geoAttachment.getExt());
            if (rep.isOK()) {
                String remoteFilePath = rep.getContent();
                /**设置文件地址**/
                geoAttachment.setFilePath(remoteFilePath);
                String id = UUID.randomUUID().toString().replace("-", "");
                geoAttachment.setId(id);
                /**保存上传记录**/
                geoAttachmentMapper.insert(geoAttachment);
                /**删除临时文件夹**/
                deleteDirectory(localTmpPathUtil.getFileTmpDirPath(geoAttachment.getMd5(), geoAttachment.getChunkSize(), false));
                return new RepEntity("200", id);
            }
        }
        return new RepEntity("400", "未知错误,请重新上传");
    }

    @Override
    public RepEntity uploadImageAndCreateThumbnail(GeoAttachment geoAttachment) {
        return null;
    }


    @Transactional
    @Override
    public RepEntity uploadWordAndTranToPdf(GeoAttachment geoAttachment) {
        if(!TextExtractor.isOfficeFile(geoAttachment.getName())){
            return new RepEntity("401","上传的不是office文件,无法转换");
        }
        // 上传word文件
        String uId = UUID.randomUUID().toString().replaceAll("-","");
        geoAttachment.setId(uId);
        RepEntity orgUploadEntity = upload(geoAttachment);
        if(orgUploadEntity.isOK()){
            // 创建tmp文件
            String tmpFilePath = localTmpPathUtil.getConvertTmpPath(geoAttachment.getMd5(),true);
            File tmpConvertFile = new File(tmpFilePath + File.separator + geoAttachment.getName());
            String convertedPdfPath = localTmpPathUtil.getConvertTmpPath(geoAttachment.getMd5(),true)+File.separator
                    + FilenameUtils.removeExtension(geoAttachment.getName())+".pdf";
            try {
                FileUtils.writeByteArrayToFile(tmpConvertFile,geoAttachment.getFileContent());
            } catch (IOException e) {
                e.printStackTrace();
            }
            File convertedPdf = new File(convertedPdfPath);
//            // 文件转化
//            FileTypeConverter.office2pdf2(tmpConvertFile, convertedPdf);
            // 上传文件
            try {
                byte[] pdfContent = FileUtils.readFileToByteArray(convertedPdf);
                geoAttachment.setFileContent(pdfContent);
                geoAttachment.setId(uId+"_pdf");
                geoAttachment.setExt("pdf");
                geoAttachment.setName(FilenameUtils.removeExtension(geoAttachment.getName())+"pdf");
                geoAttachment.setFileSize(pdfContent.length);
                upload(geoAttachment);
                return new RepEntity("200","成功!!");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }


    private byte[] File2byte(String filePath) {
        byte[] buffer = null;
        try {
            File file = new File(filePath);
            FileInputStream fis = new FileInputStream(file);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] b = new byte[1024];
            int n;
            while ((n = fis.read(b)) != -1) {
                bos.write(b, 0, n);
            }
            fis.close();
            bos.close();
            buffer = bos.toByteArray();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return buffer;
    }

    /**
     * 删除目录（文件夹）以及目录下的文件
     *
     * @param sPath 被删除目录的文件路径
     * @return 目录删除成功返回true，否则返回false
     */
    public boolean deleteDirectory(String sPath) {
        //如果sPath不以文件分隔符结尾，自动添加文件分隔符
        if (!sPath.endsWith(File.separator)) {
            sPath = sPath + File.separator;
        }
        File dirFile = new File(sPath);
        //如果dir对应的文件不存在，或者不是一个目录，则退出
        if (!dirFile.exists() || !dirFile.isDirectory()) {
            return false;
        }
        boolean flag = true;
        //删除文件夹下的所有文件(包括子目录)
        File[] files = dirFile.listFiles();
        for (int i = 0; i < files.length; i++) {
            //删除子文件
            if (files[i].isFile()) {
                flag = deleteFile(files[i].getAbsolutePath());
                if (!flag) {break;}
            } //删除子目录
            else {
                flag = deleteDirectory(files[i].getAbsolutePath());
                if (!flag) {break;}
            }
        }
        if (!flag) {return false;}
        //删除当前目录
        return dirFile.delete();
    }

    /**
     * 删除单个文件
     *
     * @param sPath 被删除文件的文件名
     * @return 单个文件删除成功返回true，否则返回false
     */
    public boolean deleteFile(String sPath) {
        boolean flag = false;
        File file = new File(sPath);
        // 路径为文件且不为空则进行删除
        if (file.isFile() && file.exists()) {
            file.delete();
            flag = true;
        }
        return flag;
    }

}
